/*
* ============================================================================
*  Name        : cameraengineprivate.cpp
*  Part of     : Symbian/S60 Camera Wrapper
*  Description : Camera engine (private) class definition
*  Version     : %version: 2 %
*
* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details.
*
* You are welcome to, but not required to, contribute any modifications that
* improve stability, usability and/or feature set of the library back to
* Symbian/S60 Camera Wrapper project, as long as binary compatibility is
* retained. See https://projects.forum.nokia.com/camerawrapper for more info.
* ============================================================================
*/
#include <fbs.h>

#include "cameraengineprivate.h"
#include "cameraengineobserver.h"

//#include "filelogger.h"

#if defined(__S60_32__) || defined(__S60_50__)
#warning "\nLinking agaist ECAM.LIB from S60 3.2/5.0 SDKs will break compatibility with S60 3.1 devices. Using S60 3.1 SDK is recommended.\n"
#endif

// hack to use CCamera::New2L constructor (see comments in ConstrucL)
typedef TInt(*TECamLibraryFunction)(MCameraObserver2&, TInt, TInt);

CCameraEnginePrivate::CCameraEnginePrivate( TInt aCameraHandle, 
                                            TInt aPriority,
                                            MCameraEngineObserver* aObserver )
: iCameraHandle( aCameraHandle ),
  iPriority( aPriority ),
  iObserver( aObserver ),
  iEngineState( EEngineNotReady ),
  iSelectedCaptureSize( -1, -1 ),   // undefined until PrepareL() call
  iSelectedCaptureFormat( CCamera::EFormatExif )
  {
  }

CCameraEnginePrivate::~CCameraEnginePrivate()
  {
  StopViewFinder();
  ReleaseViewFinderBuffer();  // releases iViewFinderBuffer
  ReleaseImageBuffer();       // releases iImageBuffer + iImageBitmap, deletes iImageData
  delete iImageBitmap;
  
  delete iAutoFocus;
  delete iAdvSettings;
  delete iCamera;
  }

CCameraEnginePrivate* CCameraEnginePrivate::NewL( TInt aCameraHandle, 
                                                  TInt aPriority, 
                                                  MCameraEngineObserver* aObserver )
  {
  CCameraEnginePrivate* self = 
    new (ELeave) CCameraEnginePrivate( aCameraHandle, aPriority, aObserver );
  CleanupStack::PushL(self);
  self->ConstructL();
  CleanupStack::Pop(self);
  return self;
  }


void CCameraEnginePrivate::ConstructL()
  {

  if ( !CCamera::CamerasAvailable() )
    {
    User::Leave( KErrHardwareNotAvailable );
    }    

  //TRAPD( v2err, iCamera = CCamera::New2L( *this, iCameraHandle, iPriority ) );
  
#if !defined(__WINS__)

  RLibrary library;

  // Try looking up the New2L constructor from ECam
  // This is a hack, but has to be done because if we link against 
  // S60 3.2 ecam.lib, the app will fail on 3.1 devices with 
  // KErrNotSupported already at startup.
  // This way, we can build with 3.1 SDK but still call CCamera::New2L() and
  // receive all those additional (eg. AF-related) events.
  
  // (3.1 devices seem to have no entry @ ordinal 17, so this works)
  
  User::LeaveIfError(library.Load(_L("ecam")));

  TECamLibraryFunction New2L = (TECamLibraryFunction)library.Lookup(17);
  TInt v2err( KErrNone );
  if( New2L )
    {    
    TRAP(v2err, iCamera = (CCamera*) New2L( *this, iCameraHandle, iPriority ));      
    }
  
  library.Close();
#else
  TInt v2err( KErrNotFound );
#endif // !(__WINS__)
  
  // construct CCamera object for S60 3.0/3.1
  if( !iCamera || v2err )
    {
    iCamera = CCamera::NewL( *this, iCameraHandle );
    // Setting up AF may fail, can be ignored
    TRAP_IGNORE( iAutoFocus = CCamAutoFocus::NewL( iCamera ) );
    }
#if !defined(__WINS__)
  else
    {
    TRAP_IGNORE( iAdvSettings = CCamera::CCameraAdvancedSettings::NewL( *iCamera ) );
    }
#endif // !__WINS__

  iImageBitmap = new (ELeave) CFbsBitmap;
    
  iCamera->CameraInfo( iCameraInfo );
  }
  
  
void CCameraEnginePrivate::ReserveAndPowerOn()
  {
  iCamera->Reserve();
  }


void CCameraEnginePrivate::ReleaseAndPowerOff()
  { 
  if( iCameraReserved )
    {
    StopViewFinder();
    FocusCancel();
    iCamera->PowerOff();
    iCamera->Release();
    iCameraReserved = EFalse;
    iSelectedCaptureSize.SetSize(-1, -1);
    }
  }
  

void CCameraEnginePrivate::StartViewFinderL( TSize& aSize )
  {
  if( iEngineState != EEngineIdle )
    User::Leave( KErrNotReady );
    
  if( 0 == (iCameraInfo.iOptionsSupported & TCameraInfo::EViewFinderBitmapsSupported) )
    User::Leave( KErrNotSupported );    
    
  if( !iCamera->ViewFinderActive() )
    {
    iCamera->StartViewFinderBitmapsL( aSize );
    }
    
  iEngineState = EEngineViewFinding;
  }
  
  
void CCameraEnginePrivate::StopViewFinder()
  {
  if( iCamera && iCamera->ViewFinderActive() )
    {
    iCamera->StopViewFinder();
    iEngineState = EEngineIdle;
    }
  }
  

void CCameraEnginePrivate::PrepareL( TSize& aCaptureSize,                                                      
                                     CCamera::TFormat aFormat )
  {
  TInt closestVar = KMaxTInt, selected = 0;
  TSize size;
  
  // No need to do anything if settings haven't changed
  if( iSelectedCaptureSize == aCaptureSize &&
      iSelectedCaptureFormat == aFormat )
    {
    return;
    }
    
  // Scan through supported capture sizes and select the closest match
  for (TInt index=0; index < iCameraInfo.iNumImageSizesSupported; index++)
    {    
    iCamera->EnumerateCaptureSizes(size, index, aFormat);
    if (size == aCaptureSize)
        {
        selected = index;
        break;
        }
    
    TSize varSz = size - aCaptureSize;
    TInt variation = varSz.iWidth * varSz.iHeight;
    if( variation < closestVar )
      {
      closestVar = variation;
      selected = index;      
      }
    }
  
  //__LOGSTR_TOFILE1("supported formats: %x", iCameraInfo.iImageFormatsSupported );
  iCamera->EnumerateCaptureSizes( aCaptureSize, selected, aFormat );
  iCamera->PrepareImageCaptureL( aFormat, selected );
  
  iSelectedCaptureSize = aCaptureSize;
  iSelectedCaptureFormat = aFormat;
  }


void CCameraEnginePrivate::CaptureL()
  {
  if( !iCameraReserved )
    User::Leave( KErrNotReady );
  iCamera->CaptureImage();
  iEngineState = EEngineCapturing;
  }
  
  
void CCameraEnginePrivate::HandleEvent( const TECAMEvent &aEvent )
  {
    
  if( aEvent.iEventType == KUidECamEventReserveComplete )
      {
      ReserveComplete( aEvent.iErrorCode );
      return;
      }
      
  if( aEvent.iEventType == KUidECamEventPowerOnComplete )
     {  
     PowerOnComplete( aEvent.iErrorCode );
     return;
     }
   
   // AUTOFOCUS RELATED EVENTS
#if !defined(__WINS__)
  switch( aEvent.iEventType.iUid )
      {
      // Set some basic settings automatically (AF Area, Focus Range)
      // after receiving a call to SetFocusMode()
      case KUidECamEventCameraSettingFocusModeUidValue:
          {
          if ( KErrNone == aEvent.iErrorCode )
                {
                iAdvSettings->SetAutoFocusArea(CCamera::CCameraAdvancedSettings::EAutoFocusTypeAuto);
                }
            else
                {
                iObserver->MceoHandleError(EErrAutoFocusMode, aEvent.iErrorCode);
                }
          break;
           }

      case KUidECamEventCameraSettingAutoFocusAreaUidValue:
          {
          if ( !aEvent.iErrorCode )
              {
              //iRange = ECameraExFRNormal;
              iAdvSettings->SetFocusRange(CCamera::CCameraAdvancedSettings::EFocusRangeNormal);              
              }
          else
              {
              iObserver->MceoHandleError(EErrAutoFocusArea, aEvent.iErrorCode);
              } 
          break;
          }
          
      case KUidECamEventCameraSettingFocusRange2UidValue:
          {
          if ( !aEvent.iErrorCode )
              {
              // Init complete - AF is activated by calling SetAutoFocusType()  
              }
          else
              {
              iObserver->MceoHandleError(EErrAutoFocusRange, aEvent.iErrorCode);
              }
              
          break;
          }
          
      case KUidECamEventCameraSettingAutoFocusType2UidValue:
          {
          if ( !aEvent.iErrorCode )
              {
              // All OK              
              }
          else
              {
              iObserver->MceoHandleError(EErrAutoFocusType, aEvent.iErrorCode);
              }
          break;
          }
        
      // Focusing completed
      case KUidECamEventCameraSettingsOptimalFocusUidValue:
          OptimisedFocusComplete( aEvent.iErrorCode );
          break;
          
      case KUidECamEventCameraSettingFocusRangeUidValue:
      // case KUidECamEventCameraSettingAutoFocusTypeUidValue:
          // ---> These are deprecated - safe to ignore          
          break;
          
      // Other events; exposure, digital zoom etc.. see ecamadvancedsettings.h
      default:
          //__LOGSTR_TOFILE2("[%x, %d]", aEvent.iEventType, aEvent.iErrorCode);
          iObserver->MceoHandleOtherEvent( aEvent );
          break;
      }
#endif // !__WINS__
  }
   

void CCameraEnginePrivate::ReserveComplete( TInt aError )
  {
  if( aError == KErrNone )
    {
    iCamera->PowerOn();
    }
  else
    {
    iObserver->MceoHandleError(EErrReserve, aError);
    }
  }


void CCameraEnginePrivate::PowerOnComplete( TInt aError )
  {
  iCameraReserved = (aError == KErrNone) ? ETrue : EFalse;
  
  if( !iCameraReserved )
    {
    iObserver->MceoHandleError(EErrPowerOn, aError);
    return;
    }
    
   // Init AF
#if !defined(__WINS__)
  if( iAdvSettings )
    {
    CCamera::CCameraAdvancedSettings::TFocusMode defaultAFMode =
      CCamera::CCameraAdvancedSettings::EFocusModeAuto;
    if( iAdvSettings->SupportedFocusModes() & defaultAFMode )
      {
      iAdvSettings->SetFocusMode( defaultAFMode );
      }
    }
  else if( iAutoFocus )
    {
    TRAPD( afErr, iAutoFocus->InitL( *this ) );
    if( afErr )
      {
      delete iAutoFocus;
      iAutoFocus = 0;
      }
    }
#endif
  
  iEngineState = EEngineIdle;
  iObserver->MceoCameraReady();
  }


void CCameraEnginePrivate::ViewFinderReady( MCameraBuffer &aCameraBuffer, 
                                            TInt aError )
  {    
  iViewFinderBuffer = &aCameraBuffer;
  
  if( aError == KErrNone )
    {    
    // Let's trust this does not leave as aError == KErrNone
    iObserver->MceoViewFinderFrameReady( aCameraBuffer.BitmapL(0) ); 
    }
  else
    {   
    iObserver->MceoHandleError( EErrViewFinderReady, aError );
    }   
  }


void CCameraEnginePrivate::ViewFinderFrameReady( CFbsBitmap& aFrame )
  {  
  iObserver->MceoViewFinderFrameReady( aFrame );
  }


void CCameraEnginePrivate::ReleaseViewFinderBuffer()
  {
  if( iViewFinderBuffer )
    {
    iViewFinderBuffer->Release();
    iViewFinderBuffer = 0;
    }
  }
  

void CCameraEnginePrivate::ReleaseImageBuffer()
  {  
  if(iImageBuffer)
    {
    iImageBuffer->Release();
    iImageBuffer = 0;
    }
  
  if(iImageBitmap)
    {
    iImageBitmap->Reset();
    }
  
  delete iImageData;
  iImageData = 0;
  }


void CCameraEnginePrivate::ImageBufferReady( MCameraBuffer &aCameraBuffer, 
                                             TInt aError)
  {      
  CFbsBitmap* bitmap = 0;
  TDesC8* data = 0;
  
  iImageBuffer = &aCameraBuffer;
  
  // Ignore leaves here, trust that aError is set correctly  
  TRAP_IGNORE( bitmap = &aCameraBuffer.BitmapL(0) );
  TRAP_IGNORE( data = aCameraBuffer.DataL(0) );
  
  HandleImageReady( bitmap, data, aError );
  }


void CCameraEnginePrivate::ImageReady( CFbsBitmap* aBitmap, 
                                       HBufC8* aData, 
                                       TInt aError )
  {
  if( aBitmap )
    {
    iImageBitmap->Duplicate( aBitmap->Handle() );
    }
  else
    {
    iImageData = aData;
    }  
  
  HandleImageReady( iImageBitmap, aData, aError );
  }

                           
void CCameraEnginePrivate::HandleImageReady( CFbsBitmap* aBitmap, 
                                             TDesC8* aData,  
                                             TInt aError )
  {    
  iEngineState = EEngineIdle;
  
  if ( aError == KErrNone )
    {
    if ( aData )
      {
      iObserver->MceoCapturedDataReady( aData );
      }
    else if( aBitmap )
      {
      iObserver->MceoCapturedBitmapReady( aBitmap );
      }
    }
  else
    {
    iObserver->MceoHandleError(EErrImageReady, aError);
    }
  }
  

void CCameraEnginePrivate::InitComplete( TInt aError )
  {
  if( aError )
    {
    iObserver->MceoHandleError( EErrAutoFocusInit, aError );
    }
  }


void CCameraEnginePrivate::OptimisedFocusComplete( TInt aError )
  {
  iEngineState = EEngineIdle;
  
  if( aError == KErrNone )
    {
    iObserver->MceoFocusComplete();
    }
  else
    {
    iObserver->MceoHandleError( EErrOptimisedFocusComplete, aError );
    }
  }
  
  
TBool CCameraEnginePrivate::IsAutoFocusSupported() const
  {
  if( !iAdvSettings )
    {    
    return (iAutoFocus) ? ETrue : EFalse;
    }
#if !defined(__WINS__)
  return ( iAdvSettings->SupportedFocusModes() &
           CCamera::CCameraAdvancedSettings::EFocusModeAuto );
#else // __WINS__
  return EFalse;
#endif // !__WINS__
  }
  
  
void CCameraEnginePrivate::StartFocusL()
    {
    if( iEngineState != EEngineIdle && 
        iEngineState != EEngineViewFinding )
      {
      return;
      }
      
#if !defined(__WINS__)
    if ( iAdvSettings )
      {
      iAdvSettings->SetAutoFocusType(CCamera::CCameraAdvancedSettings::EAutoFocusTypeSingle);
      iEngineState = EEngineFocusing;
      return;
      }
#endif // __WINS__

    if( iAutoFocus )
      {
      if( !iAFRange )
        {
        iAFRange = CCamAutoFocus::ERangeNormal;
        iAutoFocus->SetFocusRangeL( iAFRange );
        }
      iAutoFocus->AttemptOptimisedFocusL();
      iEngineState = EEngineFocusing;
      }
    }

/*
-------------------------------------------------------------------------------
Cancels an ongoing autofocus operation
-------------------------------------------------------------------------------
*/
void CCameraEnginePrivate::FocusCancel()
    {
#if !defined(__WINS__)
    if ( iAdvSettings )
      {
      iAdvSettings->SetAutoFocusType( CCamera::CCameraAdvancedSettings::EAutoFocusTypeOff );
       iEngineState = EEngineIdle;
      return;
      }
#endif // !__WINS__
    if( iAutoFocus )
      {
      iAutoFocus->Cancel();
       iEngineState = EEngineIdle;
      }
    }
  
void CCameraEnginePrivate::SupportedFocusRanges( TInt& aSupportedRanges ) const
  {
  aSupportedRanges = 0;
#if !defined(__WINS__)
  if( iAdvSettings )
    {
    aSupportedRanges = iAdvSettings->SupportedFocusRanges();
    }  
  else if( iAutoFocus )
    {
    // CCamAutoFocus doesn't provide a method for getting supported ranges :(
    // Assume everything is supported (rather optimistic...)
    aSupportedRanges = CCamAutoFocus::ERangeMacro |
                       CCamAutoFocus::ERangePortrait |
                       CCamAutoFocus::ERangeNormal |
                       CCamAutoFocus::ERangeInfinite;
    }
#endif // !__WINS__
  }


void CCameraEnginePrivate::SetFocusRange( TInt aFocusRange )
  {
#if !defined(__WINS__)
  if( iAdvSettings )
    {
    iAdvSettings->SetFocusRange( 
      (CCamera::CCameraAdvancedSettings::TFocusRange)aFocusRange );
    }
  else if( iAutoFocus )
    {
    TRAPD( focusErr, 
      iAutoFocus->SetFocusRangeL( (CCamAutoFocus::TAutoFocusRange)aFocusRange )
      );
    if( focusErr )
      {
      iObserver->MceoHandleError( EErrAutoFocusRange, focusErr );
      }
    }
    
#else // __WINS__
  iObserver->MceoHandleError( EErrAutoFocusRange, KErrNotSupported );
#endif // !__WINS__
  }
  
  
TInt CCameraEnginePrivate::AdjustDigitalZoom( TBool aTele )
  {
  TInt currentZoom = iDigitalZoom;
  if ( iCameraInfo.iMaxDigitalZoom != 0 )
    {
    if ( aTele && iDigitalZoom < iCameraInfo.iMaxDigitalZoom )
        {
        iDigitalZoom++;
        }
    else if ( !aTele && iDigitalZoom > 0 )
        {
        iDigitalZoom--;
        }
        
    if( currentZoom == iDigitalZoom )
      {
      return iDigitalZoom;
      }
      
    TRAPD(zErr, iCamera->SetDigitalZoomFactorL(iDigitalZoom));
    if(zErr == KErrNone)
      {
      return iDigitalZoom;
      }
    }
  iDigitalZoom = currentZoom;
  return KErrNotSupported;
  }
  

TInt CCameraEnginePrivate::SetExposure( TInt aExposure )
  {
  TRAPD(expErr, iCamera->SetExposureL( (CCamera::TExposure)aExposure ));
  return expErr;
  }

TInt CCameraEnginePrivate::SetFlash( TInt aFlash )
  {
  TRAPD(flsErr, iCamera->SetFlashL( (CCamera::TFlash)aFlash ));
  return flsErr;
  }
  
// eof
